#include "_generator.h"
#include "Chunk.h"
#include <assert.h>

//**											   **//
////////////////****BASE OPERATIONS****////////////////
//**											   **//

void _Generator::CleanUp() {
	for(int i = 0;i < MapH * MapW;i++) {
		Generated[i].CleanUp();
	}
	
	if(Generated) { delete [] Generated; Generated = NULL; }
	//if(CurrentBiome) { delete CurrentBiome; CurrentBiome = NULL; }
}

void _Generator::ShutDown() {
	PBIOME ToDelete = NULL;
	while(BiomeList.size() > 0) {
		ToDelete = BiomeList.getByIndex(0);
		BiomeList.remove(0);
		if(ToDelete)
			delete ToDelete;
		ToDelete = NULL;
	}
	Buffer = NULL;
	//
}

void _Generator::HandleMessage(int const & Msg,void * MsgDat) {
	switch(Msg) {
	case RECHECK_CURRENT_HEIGHT:
		*((int*)MsgDat) = ccy * CHUNK_HEIGHT + cby;
		break;
	}
}

void _Generator::FillChunk(int const & ChunkX,int const & ChunkY) {
	char * NewBuffer = new char[128];
	current = &(*pMap)[ChunkX + ChunkY * MapH];

	//Open the old file, write basic data
	std::ofstream out;
	out.open(current->getFileName(), std::ios::out);
	sprintf_s(NewBuffer,64," 2500 0 %i %i %i",BLOCK_TYPE_DIRT >> 8,BLOCK_TRAIT_WEIGHT >> 16,BLOCK_TRAIT_POW_POWER1 >> 24);
	out.write( " 20    ", 7);
	out << NewBuffer;
	out.close();
	//Reload it into memory
	current->Load(Buffer);
	delete NewBuffer;
}

void _Generator::ClearChunk(int const & ChunkX,int const & ChunkY) {
	char * NewBuffer = new char[128];
	current = &(*pMap)[ChunkX + ChunkY * MapH];

	//Open the old file, write basic data
	std::ofstream out;
	out.open(current->getFileName(), std::ios::out);
	sprintf_s(NewBuffer,64," 2500 0 %i %i %i",BLOCK_TYPE_AIR >> 8,BLOCK_TRAIT_DIRDOWN >> 16,BLOCK_TRAIT_POW_POWER1 >> 24);
	out.write( " 20    ", 7);
	out << NewBuffer;
	out.close();
	//Reload it into memory
	current->Load(Buffer);
	delete NewBuffer;
}

//**											   **//
////////////////****LOW LEVEL TOOLS****////////////////
//**											   **//

Block * _Generator::GetBlock(int const & a_bX,int const & a_bY) {
#ifdef _DEBUG
	assert( a_bX < CHUNK_WIDTH && a_bX >= 0 && a_bY < CHUNK_HEIGHT && a_bY >= 0);
#endif
	return &Buffer[a_bX + (a_bY * CHUNK_HEIGHT)];
}

void _Generator::AddBlock(int const & a_X,int const & a_Y,Block * a_B) {
#ifdef _DEBUG
	assert( a_X < CHUNK_WIDTH && a_X >= 0 && a_Y < CHUNK_HEIGHT && a_Y >= 0);
#endif
	AddBlock(a_X,a_Y,a_B->GetData_s());
}

void _Generator::AddBlock(int const & a_X,int const & a_Y,unsigned int const & a_Dat) {
#ifdef _DEBUG
	assert( a_X < CHUNK_WIDTH && a_X >= 0 && a_Y < CHUNK_HEIGHT && a_Y >= 0);
#endif
	Buffer[a_X + (a_Y * CHUNK_HEIGHT)].SetData_s(a_Dat);
}

//Adds multiple blocks in one chunk column
// A negative amount = UP
// A positive amount = DOWN
void _Generator::AddBlockMulti(int const & a_X,int const & a_Y,unsigned int const & a_Dat,int Amount) {
#ifdef _DEBUG
	assert( a_X < CHUNK_WIDTH && a_X >= 0 && ((a_Y < CHUNK_HEIGHT && a_Y >= 0) || (a_Y == CHUNK_HEIGHT && Amount < 0)));
#endif

	if( a_X >= CHUNK_WIDTH || a_X < 0 ||
		((a_Y >= CHUNK_HEIGHT || a_Y < 0) && Amount > 0))
		int i = 0;

	cbx = a_X;
	cby = a_Y;
	int Dir = 1;
	if(Amount < 0) { Dir = -1; cby--; }
	for(int i = abs(Amount);i > 0 && cby >= 0 && cby <= CHUNK_HEIGHT;i -= abs(Dir),cby += Dir) {
		AddBlock(cbx,cby,a_Dat);
	}
	if(Amount < 0 ) cby++;
}

void _Generator::AddBlockMultiChunk(int const & a_bX,int const & a_bY,int const & a_cX,int const & a_cY,unsigned int const & a_Data,int Amount) {
	

	cbx = a_bX;
	cby = a_bY;
	ccx = a_cX;
	ccy = a_cY;

	DoubleCheckValues();
	
#ifdef _DEBUG
	assert(cbx < CHUNK_WIDTH && cbx >= 0);
	assert((cby < CHUNK_HEIGHT && cby >= 0) || (cby == CHUNK_HEIGHT && Amount < 0));
	assert(ccx < MapW && ccx >= 0);
	assert(ccy < MapH && ccy >= 0);
#endif

	if(current != &(*pMap)[ccx + ccy * MapH]) {
		if(current)
			current->Save();
		current = &(*pMap)[ccx + ccy * MapH];
		current->Load(Buffer);
	}

	int Dir = 1;
	if(Amount < 0) { 
		Dir = -1; 
		cby--;
		if(cby < 0) {
			cby = CHUNK_HEIGHT-1;
			ccy--;
			if(ccy < 0) {
				ccy = 0;
				cby = 0;
			}
		}
	}
	for(int i = abs(Amount);i > 0;i -= abs(Dir),cby += Dir) {
		if(cby >= CHUNK_HEIGHT || cby < 0) {
			if(ccy + Dir < MapH && ccy + Dir >= 0)
				ccy += Dir;
			else 
				break;
			if(Amount > 0)
				cby = 0;
			else cby = CHUNK_HEIGHT-1;
			current->Save();
			current->Change();
			current = &(*pMap)[ccx + ccy * MapH];
			current->Load(Buffer);
		}
		AddBlock(cbx,cby,a_Data);
	}
	current->Change();
	current->Save();
	if(Amount < 0 ) cby++;
}

void _Generator::ApplyLayer(int TypeToReplace,int TypeToApply,int Depth,bool & ToolTrigger,int Max,int Min) {
	int tempcbx = cbx;
	int curccy = ccy;
	cbx = cby = 0;
	FindNearestOpenGround(0,ccx,0);
	for(cbx = 0;cbx < CHUNK_WIDTH;cbx++) {
		FindNearestOpenGround(cbx,ccx,ccy-1);
		if(cby >= CHUNK_HEIGHT-1 || cby <= 0) {
			FindNearestOpenGround(cbx,ccx,ccy-1);
		}

#ifdef _DEBUG
		assert( cbx < CHUNK_WIDTH && cbx >= 0 && cby < CHUNK_HEIGHT && cby >= 0);
#endif
		AddBlockMultiChunk(cbx,cby,ccx,ccy,TypeToApply,1);/*(rand() % Max) + Min);*/
		
		
	}
	FindNearestOpenGround(tempcbx,ccx,0);
}

int _Generator::FindOpenGroundHeight(int const & x,bool Save) {
	int ArrPos;
	int TempData;

	cbx = (x  - (x / (CHUNK_SIZE_W)) * (CHUNK_SIZE_W)) / BLOCK_TEXTURE_SIZE;
	cby = 0;
	ccx = (x / (CHUNK_SIZE_W));
	ccy = 0;

#ifdef _DEBUG
	assert(cbx < CHUNK_WIDTH && cbx >= 0);
	assert(ccx < MapW && ccx >= 0);
#endif

	if(current->IsLoaded() && Save)
		current->Unload();
	current = NULL;
	current = &(*pMap)[ccx + (ccy * MapH)];
	current->Load(Buffer);

	bool F = false;
	int CurDir = 0;
	short PrevMoved = 0,Moved = 0;
	for(;ccy < MapH && ccy >= 0;ccy+=CurDir,Moved+=CurDir) {
		ArrPos = cbx + (cby * CHUNK_HEIGHT);
		TempData = Buffer[ArrPos].GetData_noAsset();
		//Picks which lastXDir to look at next
		if ( TempData > BLOCK_SOLIDS )
		{ 
			CurDir = 1; }
		else if( ccy > 0 )
		{ 
			CurDir = -1; }
		else
		{
			break;}

		//Prevents Infinite Loop
		if( abs(Moved) < abs(PrevMoved) ) 
		{ 
			break; }
		PrevMoved = Moved;

		if( Moved != 0 ) {
			current->Unload();
			current = NULL;
			current = &(*pMap)[ccx + (ccy * MapH)];
			current->Load(Buffer);
		}

#ifdef _DEBUG
		assert( cbx < CHUNK_WIDTH && cbx >= 0 && cby < CHUNK_HEIGHT && cby >= 0);
#endif
		F = FindSolidInCol(cbx,cby);
		if(F)
			break;
	}

	return (cby * BLOCK_TEXTURE_SIZE + ccy * CHUNK_SIZE_H);
}

//Allowed to move
// Unstable
//Checks in one block column with the ability to move up/down chunks based on what it starts at
void _Generator::FindNearestOpenGround(int const & a_bX,int const & a_cX,int const & a_cY) {
	cbx = a_bX;
	cby = 0;
	ccx = a_cX;
	ccy = a_cY;
	DoubleCheckValues();

#ifdef _DEBUG
	assert(cbx < CHUNK_WIDTH && cbx >= 0);
	assert(ccx < MapW && ccx >= 0);
	assert(ccy < MapH && ccy >= 0);
#endif

	
	if(current != &(*pMap)[ccx + ccy * MapH]) {
		if(current)
			current->Save();
		current = &(*pMap)[ccx + ccy * MapH];
		current->Load(Buffer);
	}

	bool F = false;
	int CurDir = 0;
	short PrevMoved = 0,Moved = 0;
	for(;ccy < MapH && ccy >= 0;ccy+=CurDir,Moved+=CurDir) {

		//Picks which lastXDir to look at next
		if ( Buffer[cbx + cby * CHUNK_HEIGHT] == DEFAULT_AIR_BLOCK )
		{ CurDir = 1; }
		else if( ccy > 0 )
		{ CurDir = -1; }
		else
		{break;}

		//Prevents Infinite Loop
		if( abs(Moved) < abs(PrevMoved) ) 
		{ break; }
		PrevMoved = Moved;

		if( Moved != 0 ) {
#ifdef _DEBUG
				assert( ccx < MapW && ccx >= 0 && ccy < MapH && ccy >= 0);
#endif
			current->Save();
			current = &(*pMap)[ccx + ccy * MapH];
			current->Load(Buffer);
		}
		#ifdef _DEBUG
			assert( cbx < CHUNK_WIDTH && cbx >= 0 && cby < CHUNK_HEIGHT && cby >= 0);
		#endif
		F = FindSolidInCol(cbx,cby);
		if(F)
			break;
	}
}

//These are the Same as FindNextMatch, except they accept a starting search position
bool _Generator::FindMatch(int const & a_X,int const & a_Y,Block * a_B) {
	return FindMatch(a_X,a_Y,a_B->GetData_s());
}

bool _Generator::FindMatch(int const & a_X,int const & a_Y,unsigned int const & a_Dat) {
#ifdef _DEBUG
	assert( a_X < CHUNK_WIDTH && a_X >= 0 && a_Y < CHUNK_HEIGHT && a_Y >= 0);
#endif
	int yReset = a_Y;
	for(cbx = a_X;cbx < CHUNK_WIDTH;cbx++) {
		for(cby = yReset;cby < CHUNK_HEIGHT;cby++) {
			if( Buffer[cbx + (cby * CHUNK_HEIGHT)].GetData_noAsset() == a_Dat ) {
				return true;
			}
		}
		if(yReset != 0) yReset = 0;
	}
	cbx = a_X;
	cby = a_Y;
	return false;
}

bool _Generator::FindMatchInCol(int const & a_X,int const & a_Y,unsigned int const & a_Dat) {
#ifdef _DEBUG
	assert( a_X < CHUNK_WIDTH && a_X >= 0 && a_Y < CHUNK_HEIGHT && a_Y >= 0);
#endif
	int TempData;
	int ArrPos;
	if(a_X < CHUNK_WIDTH && a_X >= 0 && a_Y < CHUNK_HEIGHT && a_Y >= 0) {
		for(cby = a_Y;cby < CHUNK_HEIGHT;cby++) {
			ArrPos = a_X + (cby * CHUNK_HEIGHT);
			TempData = Buffer[ArrPos].GetData_noAsset();
			if( TempData == a_Dat ) {
				return true;
			}
		}
	}
	cby = a_Y;
	return false;
}

bool _Generator::FindSolidInCol(int const & a_X,int const & a_Y) {
	
#ifdef _DEBUG
	assert( a_X < CHUNK_WIDTH && a_X >= 0 && a_Y < CHUNK_HEIGHT && a_Y >= 0);
#endif
	int ArrPos;
	if(a_X < CHUNK_WIDTH && a_X >= 0 && a_Y < CHUNK_HEIGHT && a_Y >= 0) {
		for(cby = a_Y;cby < CHUNK_HEIGHT;cby++) {
			ArrPos = a_X + (cby * CHUNK_HEIGHT);
			if( Buffer[ArrPos].GetType_s() > BLOCK_SOLIDS ) {
				return true;
			}
		}
	}
	cby = a_Y;
	return false;
}
//**											   **//
////////////////****MID LEVEL TOOLS****////////////////
//**											   **//

//Starts at current position going down the y collumn
// if it reaches the end of the y collumn it goes to the next x (row) and continues the search
//	It returns false if it does not find a match
//	It returns true and sets the cbx, and the cby to the position of the block if it does find a match
bool _Generator::FindNextMatch(Block * a_B) {
	return FindMatch(cbx,cby,a_B->GetData_s());
}

bool _Generator::FindNextMatch(unsigned int const & a_Dat) {
	return FindMatch(cbx,cby,a_Dat);
}

void _Generator::ApplyTopLayer(int TypeToReplace,int TypeToApply,bool & ToolTrigger,int Max,int Min) {
	ApplyLayer(TypeToReplace,TypeToApply,0,ToolTrigger,Max,Min);
}

void _Generator::SafeStep(Vector2D & C, int const & x,int const & y) {

	bool Load = false;

	C.x += x;
	if(C.x >= CHUNK_WIDTH && ccx < MapW-1) {
		C.x = 0;
		ccx++;
		Load = true;
	}
	else if(C.x >= CHUNK_WIDTH && ccx >= MapW-1)
		C.x = CHUNK_WIDTH-1;
	
	if(C.x < 0 && ccx > 0) {
		C.x = CHUNK_WIDTH-1;
		ccx--;
		Load = true;
	}
	else if(C.x < 0 && ccx <= 0)
		C.x = 0;

	C.y += y;
	if(C.y >= CHUNK_HEIGHT && ccx < MapH-1) {
		C.y = 0;
		ccy++;
		Load = true;
	}
	else if(C.y >= CHUNK_HEIGHT && ccy >= MapH-1)
		C.y = CHUNK_HEIGHT-1;
	
	if(C.y < 0 && ccy > 0) {
		C.y = CHUNK_HEIGHT-1;
		ccy--;
		Load = true;
	}
	else  if(C.y < 0 && ccy <= 0)
		C.y = 0;
	
	if(Load) {
		if(current)
			current->Unload();
		current = &(*pMap)[ccx + ccy * MapH];
		current->Load(Buffer);
	}
}

void _Generator::LevelGround(int CurHeight) {
	int * tempHeight = new int(0);
	FindSolidInCol(cbx,0);
	//Get the current height
	HandleMessage(RECHECK_CURRENT_HEIGHT,(void*)tempHeight);
	//With blocks, 0 = top    CHUNK_HEIGHT-1 = bottom, so < than means higher
	//	If the current height is above the old height
	if(CurHeight < (*tempHeight)) {
	//		Set all blocks between the heights to DIRT
		AddBlockMultiChunk(cbx,cby,ccx,ccy,DEFAULT_DIRT_BLOCK,CurHeight-(*tempHeight));
	}
	//	Else IF the current height is below the old height
	else if(CurHeight > (*tempHeight)) {
	//		Set all blocks between the heights to AIR
		AddBlockMultiChunk(cbx,cby,ccx,ccy,DEFAULT_AIR_BLOCK,CurHeight-(*tempHeight));
	}
}

//**											    **//
////////////////****HIGH LEVEL TOOLS****////////////////
//**											    **//

//Melding Functions
// 1-2 seconds
void _Generator::Mesh() {
	cbx = 0;
	cby = 0;
	int Movement;

	if(ccx > 0) {
		FindNearestOpenGround(CHUNK_WIDTH-1,ccx-1,ccy);
		ccx++;
	}
	else
		FindNearestOpenGround(cbx,ccx,ccy);
	
	if(ccx > 0) {
		
		int PrevChunkY = ccy;
		
		FindNearestOpenGround(0,ccx,0);
		while(ccy > PrevChunkY && cby != 0) {
			//Fill - fill with dirt
			FillChunk(ccx,ccy);
			
			FindNearestOpenGround(0,ccx,ccy-1);

			if(ccy > PrevChunkY && cby == 0) {
				ccy--;
				cby = CHUNK_HEIGHT;
				if(current != &(*pMap)[ccx + ccy * MapH]) {
					current = &(*pMap)[ccx + ccy * MapH];
					current->Load(Buffer);
				}
			}
			else break;
		}

		while(ccy < PrevChunkY) {
			//Clear - Fill with air
			ClearChunk(ccx,ccy);

			FindNearestOpenGround(0,ccx,ccy);

			if(ccy >= PrevChunkY)
				break;
		}
			
		if(current != &(*pMap)[ccx + ccy * MapH]) {
			current = &(*pMap)[ccx + ccy * MapH];
			current->Load(Buffer);
		}

		Block_Info * PrevCol = Generated[ccx-1 + (ccy * MapH)].B.FindSolidInCol('r');

		if(PrevCol) {
			Movement = PrevCol->Y - cby;
			if(PrevCol->Y - cby < 0) {
				AddBlockMulti(cbx,cby,DEFAULT_DIRT_BLOCK,Movement);		
			}
			else if(PrevCol->Y - cby > 0) {
				AddBlockMulti(cbx,cby,DEFAULT_AIR_BLOCK,Movement);
			}
		}
		current->Save();
		delete PrevCol;
		//Generated[ccx + ccy * MapH].Reset(ccx,ccy,Buffer);
	}
}

void _Generator::CompleteBorders() {
	for(int i = 0;i < MapH;i++) {
		if((*pMap)[ccx + i * MapH].NeedSave()) {
			current = &(*pMap)[ccx + i * MapH];
			current->Load(Buffer);
			if(ccx >= MapW || i >= MapH)
				break;
			Generated[ccx + i * MapH].Reset(ccx,i,Buffer);
			current->Saved();
		}
	}
}

//Shaping Functions
// 2 seconds
void _Generator::RandomizeGroundLevel() {
	int * CurrentHeight = new int(0);

	ccy = 0;
	cbx = 0,cby = 0;
	
	//This function shoudl be used sparingly
	FindNearestOpenGround(cbx,ccx,ccy);

	//Find the current height, world Block Y position(0-999), (not local 0-49)
	HandleMessage(RECHECK_CURRENT_HEIGHT,(void*)CurrentHeight);	

//*** Rule List ***///
		//This grabs GroundData as the rule set for this generation method
		int Rules = CurrentBiome->ToolData[E_TOOL_RANDOM];

		//The Variation counter
		int Var = 0;
		//The Variation Rule = EX_GET_FIRST_BYTE(Rules);
		//The Movement counter
		int Move = 0;
		//The Movement Rule = GET_SECOND_AS_FIRST_BYTE(Rules);
		//The Frequency counter
		int Freq = 0;
		//The Frequency Rule = GET_THIRD_AS_FIRST_BYTE(Rules);
		//The lastXDir that the generator is trying to add blocks in
		int Dir = rand() % 2 + 1;
		//Used to tell if the generator needs to halt an action
		bool Wait = false;
//*** Rule List End ***///



//**--**// TOOL BEGIN //**--**//

	Vector3D Heights[CHUNK_WIDTH];
	Heights[cbx].y = (float)cby;
	Heights[cbx].z = (float)ccy;

	Tools[E_TOOL_RANDOM] = true;
	//Activate the tool, then start it
	while(Tools[E_TOOL_RANDOM]) {

//** LEVEL GROUND LOGIC BEGIN **//
		/*LevelGround((*CurrentHeight));
		HandleMessage(RECHECK_CURRENT_HEIGHT,(void*)CurrentHeight);*/
//** LEVEL GROUND LOGIC END **//

//** RULE LOGIC BEGIN **//

		//If the lastXDir has already changed and its not time to change it again
		// then wait until its time to change it again
		if( !Wait ) {
			if ( Var < EX_GET_FIRST_BYTE(Rules) ) 
				Var ++;
			else {
				Dir = rand() % 2 + 1;
				Wait = true;
			}
		}
		
		//If the frequency reaches 100% then make another move,
		// and allow variation to possibly switch lastXDir
		Freq += rand() % GET_THIRD_AS_FIRST_BYTE(Rules) + 1;
		if(Freq >= 100) {
			//Pick an amount to move based off your maximum amount
			// MoveRule == Maximum move amount per move
			Move = rand() % GET_SECOND_AS_FIRST_BYTE(Rules) + 1;

			//Move in the lastXDir that is currently set
			switch(Dir) {
			case 1: // UP
				//If this move is still within the chunks boundaries, just do a normal move
				Heights[cbx].y -= Move;
				while(Heights[cbx].y < 0) {
					Heights[cbx].y = Heights[cbx].y + CHUNK_HEIGHT;
					Heights[cbx].z = (Heights[cbx].z - 1);
					if(Heights[cbx].z < 0) {
						Heights[cbx].y = 0;
						Heights[cbx].z = 0;
						break;
					}
				}
				//AddBlockMulti(cbx,cby,DEFAULT_DIRT_BLOCK,Move*-1);
				break;
					
			case 2: // DOWN
				//Same as above except for the chunk below
				Heights[cbx].y += Move;
				while(Heights[cbx].y >= CHUNK_HEIGHT) {
					Heights[cbx].y = Heights[cbx].y - CHUNK_HEIGHT;
					Heights[cbx].z = (Heights[cbx].z + 1);
					if(Heights[cbx].z >= MapH) {
						Heights[cbx].y = CHUNK_HEIGHT-1;
						Heights[cbx].z = (float)MapH-1;
						break;
					}
				}
				break;

			//If the lastXDir has messed up, do nothing
			default: break;
			}

			//Reset values
			Freq = 0;
			//If the lastXDir has changed since last time, reset variation wait flag
			// and reset the Variation itterator
			if(Wait) {
				Var = 0;
				Wait = false;
			}
		}
//** RULE LOGIC END **//

//** FIND NEW CHUNK LOGIC BEGIN **//
		//Move to the next block column

		Heights[cbx].x = true;
		
		cbx++;
		//Equal to the previous height
		if(cbx < CHUNK_WIDTH) {
			Heights[cbx].y = Heights[cbx-1].y;
			Heights[cbx].z = Heights[cbx-1].z;
		}

		//If the current block column is past the chunks column width, GOTO FIND NEW CHUNK LOGIC BEGIN
		// else GOTO FIND NEW CHUNK LOGIC END
		if ( cbx >= CHUNK_WIDTH ) {
			//Set this chunks border now that its done
			//Generated[ccx + ccy * MapH].Reset(ccx,ccy,Buffer);
			
			//Reset the current block column to the beginning
			cbx = 0;

			Tools[E_TOOL_RANDOM] = false;
		}

		HandleMessage(RECHECK_CURRENT_HEIGHT,(void*)CurrentHeight);
//** FIND NEW CHUNK LOGIC END **//
	}

	//Check first lastXDir
	int NextDir = -1;
	int ArrHeight = 0;
	bool NewFound = false;
	bool Complete = false;
	Vector2D SendData;
	Tools[E_TOOL_RANDOM] = true;
	while(Tools[E_TOOL_RANDOM] && !Complete) {
		Complete = true;
		for(int i = 0;i < CHUNK_WIDTH;i++) {
			if(Heights[i].x) {
				if( !FindSolidInCol(i,0) ) {
					cby = CHUNK_HEIGHT;
				}

				if(Heights[i].z != ccy && !NewFound) {
					NextDir = (int)Heights[i].z;
					NewFound = true;
				}
				
				if(Heights[i].z < ccy) {
					ArrHeight = 0;
				}
				else if(Heights[i].z > ccy) {
					ArrHeight = CHUNK_HEIGHT;
				}
				else
					ArrHeight = (int)Heights[i].y;
					
				if(abs(ArrHeight-cby) != 0) {
					if(ArrHeight > cby) {
						SendData.x = DEFAULT_AIR_BLOCK;
						SendData.y = 1;
					}
					else {
						SendData.x = DEFAULT_DIRT_BLOCK;
						SendData.y = -1;
					}
					AddBlockMulti(i,cby,(int)SendData.x,(int)(abs(ArrHeight-cby)*SendData.y));
				}


				if(cby + ccy * CHUNK_HEIGHT == Heights[i].y + Heights[i].z * CHUNK_HEIGHT)
					Heights[i].x = false;
				Complete = false;
			}
		}
		NewFound = false;
		//Get next chunk
		if(NextDir >= 0 && NextDir < MapH) {			

			current->Save();
			current->Change();

			ccy = NextDir;
			NextDir = -1;

			current = &(*pMap)[ccx + ccy * MapH];
			current->Load(Buffer);
		}
		else if(Complete){
			Tools[E_TOOL_RANDOM] = false;
		}
	}
	current->Save();
	current->Change();

//**--**// TOOL END //**--**//
}

void _Generator::FlattenGroundData() {
//	int * CurrentHeight = new int(0);
//	ccy = 0;cbx = 0,cby = 0;
//	
//	//This function shoudl be used sparingly
//	FindNearestOpenGround(cbx,ccx,ccy);
//
//	//Find the current height, world Block Y position(0-999), (not local 0-49)
//	HandleMessage(RECHECK_CURRENT_HEIGHT,(void*)CurrentHeight);
//
////**--**// TOOL BEGIN //**--**//
//	Tools[E_TOOL_FLAT] = true;
//	//Activate the tool, then start it
//	while(Tools[E_TOOL_FLAT]) {
//////** LEVEL GROUND LOGIC BEGIN **//
//		LevelGround((*CurrentHeight));
//		HandleMessage(RECHECK_CURRENT_HEIGHT,(void*)CurrentHeight);
////** LEVEL GROUND LOGIC END **//
//		//Move to the next block column
//		cbx++;
//		//If the current block column is past the chunks column width, GOTO FIND NEW CHUNK LOGIC BEGIN
//		// else GOTO FIND NEW CHUNK LOGIC END
//		if ( cbx >= CHUNK_WIDTH ) {
//			//Set this chunks border now that its done
//			//Generated[ccx + ccy * MapH].Reset(ccx,ccy,Buffer);
//			//Reset the current block column to the beginning
//			cbx = 0;
//			//Save this chunk
//			current->Save();
//			Tools[E_TOOL_FLAT] = false;
//		}
//	}
////**--**// TOOL END //**--**//
}

void _Generator::CaveWalk() {
	FindNearestOpenGround(CHUNK_WIDTH/2,ccx,0);

	//Save starting chunk position
	Vector2D Past(ccx,ccy);

	Vector2D Center(0,0);
	Vector2D OldCenter(0,0);

	//Change to accept rule later
	int MaxPathLength = 5;
	//Change to accept rule later
	int MaxRadius = 5;
	//Change to accept rule later
	int MinRadius = 1;

	/*int BlockArPos;
	int ChunkArPos;*/
	int CurRadius = (rand() % MaxRadius) + MinRadius;
	int Move = CurRadius;
	int Special = 1;
	//Move = CurRadius * NewRadius
	int x = 0,y = 0;
	Center = Vector2D(cbx,cby);
	for(int i = 0;i < MaxPathLength;i++) {
		
		y = (rand() % Move) + 1;
		x = Move + y * ((rand() % 2) - 1);
		SafeStep(Center,x,y);

		OldCenter = Center;

		//Makes sure that the radius is an odd number
		if(CurRadius % 2) CurRadius++;
		SafeStep(Center,CurRadius*-1,0);
		for(int j = 0;j < ((CurRadius * 2)+1);j++) {
			AddBlockMultiChunk((int)Center.x,(int)Center.y,(int)ccx,(int)ccy,DEFAULT_AIR_BLOCK,Special);
			if(j < CurRadius) {
				Special+=2;
			}
			else if(j > CurRadius) {
				Special-=2;
			}
			SafeStep(Center,1,-1);
		}

		Special = 1;
		Move = CurRadius + (rand() % MaxRadius) + MinRadius;
		CurRadius = Move - CurRadius;
		Center = OldCenter;
	}
	current->Save();
	//Return to starting chunk position
	if(current) {
		current->Unload();
	}
	ccx = (int)Past.x;
	ccy = (int)Past.y;
	current = &(*pMap)[ccx + ccy * MapH];
	current->Load(Buffer);
}

//Cosmetic Functions
// <.5 seconds
void _Generator::ApplyGrass() {
	int Rules = CurrentBiome->ToolData[E_TOOL_GRASS];
	ApplyTopLayer(DEFAULT_DIRT_BLOCK,DEFAULT_GRASS_BLOCK,Tools[E_TOOL_STONE],EX_GET_FIRST_BYTE(Rules),GET_SECOND_AS_FIRST_BYTE(Rules));
}

// <.5 seconds
void _Generator::ApplyStone() {
	int Rules = CurrentBiome->ToolData[E_TOOL_STONE];
	ApplyTopLayer(DEFAULT_DIRT_BLOCK,DEFAULT_ROCK_BLOCK,Tools[E_TOOL_GRASS],EX_GET_FIRST_BYTE(Rules),GET_SECOND_AS_FIRST_BYTE(Rules));
}

//Object Functions
void _Generator::PlaceWinBlock() {
	FindNearestOpenGround(48,MapW-1,0);
	AddBlock(cbx,cby,WIN_BLOCK);
	current->Unload();
	
	current = NULL;
}

// 1.5 seconds
void _Generator::PlaceTrees() {
	cbx = 0;

	int Rules = CurrentBiome->ToolData[E_TOOL_TREES];
	
	//The Clustering Rule
	short ClusRule = EX_GET_FIRST_BYTE(Rules);

	short Den = 0;
	//The Density Rule
	short DenRule = GET_SECOND_AS_FIRST_BYTE(Rules);

	//The Height Rule
	short HeightRule = GET_THIRD_AS_FIRST_BYTE(Rules);

	//The Leaf Size Rule
	short LeafSRule = GET_FOURTH_AS_FIRST_BYTE(Rules);

	Tools[E_TOOL_TREES] = true;

	int TreeCount = (int)(CHUNK_WIDTH / ClusRule);
	Vector3D * AllTrees = new Vector3D[TreeCount]();
	TreeCount = 0;

	while(Tools[E_TOOL_TREES]) {
		if(Den < 100) {
			Den += rand() % DenRule + 1;
		}
		else if(TreeCount < (CHUNK_WIDTH / ClusRule)) {
			//Place Tree
			FindNearestOpenGround(cbx,ccx,ccy-1);
			if( FindMatchInCol(cbx,0,DEFAULT_GRASS_BLOCK) ) {
				AllTrees[TreeCount].x = (float)(rand() % HeightRule + 3);
				AllTrees[TreeCount].y = cbx;
				AllTrees[TreeCount].z = AllTrees[TreeCount].x;
				AddBlockMultiChunk(cbx,cby,ccx,ccy,DEFAULT_TREE_BLOCK,-(int)AllTrees[TreeCount].x);
				TreeCount++;
				Den = 0;
			}
		}

		cbx++;
		if(cbx >= CHUNK_WIDTH) {
			//Save the info of the chunk that was generated so it can be referenced by other chunks
			//Generated[ccx + ccy * MapH].Reset(ccx,ccy,Buffer);

			//Reset the current block column to the beginning
			cbx = 0;
			Tools[E_TOOL_TREES] = false;
		}
	}
	PlaceLeaves(AllTrees,TreeCount);
	delete AllTrees;
}

// <1 second
void _Generator::PlaceLeaves(Vector3D * AllTrees,int const & TreeCount) {
	/*static int k = 0;
	int Radius = 0;
	int count = 0;
	int Top;*/
	int MaxDepth;
	int LeafWidth;
	for(int i = 0;i < TreeCount;i++) {
		if(((int)AllTrees[i].x)%2) {
			AllTrees[i].z++;
		}
		MaxDepth = (int)AllTrees[i].z / 4;
		LeafWidth = (int)AllTrees[i].z - (rand() % MaxDepth) + 1;

		cbx = (short)AllTrees[i].y;
		
		FindNearestOpenGround(cbx,ccx,ccy-1);
		
		cbx -= (int)AllTrees[i].z / 2;
		cby += MaxDepth;

		for(int i = 0;i < LeafWidth;i++) {
			AddBlockMultiChunk(cbx,cby,ccx,ccy,DEFAULT_LEAF_BLOCK,1);
			cbx++;
			cby--;
			if(i < (LeafWidth) / 2) {
				if(i%2)
					cby--;
			}
			else
				if(i%2)
					cby++;
		}
		
		////Radius = (rand() % (((int)AllTrees[i].z-1) / 2)) + 2;
		///*AddBlockMultiChunk(cbx,cby - Radius,ccx,ccy,DEFAULT_LEAF_BLOCK,Radius * 2);
		//Top = cby+1;
		//cbx = cbx - Radius;*/
		//count = 0;
		//k = 0;
		
		/*for(int j = Radius*2;j > 0;j--) {
			k++;
			if(j <= Radius)
				count++;
			else count--;
			
			cby = Top;
			cbx++;
			if(cbx >= CHUNK_WIDTH) {
				cbx -= CHUNK_WIDTH;
				if(ccx+1 < MapW) {
					ccx++;
				}
				else 
					break;
			}

			AddBlockMultiChunk(cbx,cby - count,ccx,ccy,DEFAULT_LEAF_BLOCK,count * 2);
			
		}*/
	}
}

// 1 second
void _Generator::PlaceOre1() {

	int Rules = CurrentBiome->ToolData[E_TOOL_ORE1];
	
	short MaxRadius = EX_GET_FIRST_BYTE(Rules);
	if(MaxRadius > CHUNK_WIDTH/2)
		MaxRadius = CHUNK_WIDTH/2;
	MaxRadius = rand() & MaxRadius + 1;

	Tools[E_TOOL_ORE1] = true;

	FindNearestOpenGround(CHUNK_WIDTH/2,ccx,0);
	while(Tools[E_TOOL_ORE1]) {
		int count = 0;
		cbx = CHUNK_WIDTH/2 - MaxRadius;
		for(int i = MaxRadius*2-1;i > 0;i--) {
			if(i >= MaxRadius)
				count++;
			else count--;

			AddBlockMultiChunk(cbx,cby+1,ccx,ccy,DEFAULT_ORE1_BLOCK,count);
			FindNearestOpenGround(cbx+1,ccx,0);
		}
		Tools[E_TOOL_ORE1] = false;
	}
}

//3-5 seconds
void _Generator::LimiterLayer() {
	//cbx,cby,ccx,ccy,amount
	int temp;
	FindNearestOpenGround(0,0,0);
	
	temp = ccy;

	ccy--;
	AddBlockMultiChunk(0,0,0,ccy,LIMITER_BLOCK,(MapH - ccy) * CHUNK_HEIGHT);

	FindNearestOpenGround(CHUNK_WIDTH-1,MapW-1,0);
	
	temp = temp > ccy ? temp : ccy;

	ccy--;
	AddBlockMultiChunk(CHUNK_WIDTH-1,0,MapW-1,ccy,LIMITER_BLOCK,(MapH - ccy) * CHUNK_HEIGHT);

	cbx = 0;
	cby = CHUNK_HEIGHT-1;
	ccx = 0;ccy = temp+3;
	if(ccy >= MapH) ccy = MapH-1;
	if(current)
		current->Save();
	current = &(*pMap)[ccx + ccy * MapH];
	current->Load(Buffer);
		
	for(int j = 0;j < CHUNK_WIDTH * MapW;j++) {
		AddBlock(cbx,cby,LIMITER_BLOCK);
		cbx++;
		if(cbx >= CHUNK_WIDTH && j+1 != CHUNK_WIDTH * MapW) {
			cbx = 0;
			ccx++;
			current->Save();
			current = &(*pMap)[ccx + ccy * MapH];
			current->Load(Buffer);
		}
	}
	current->Save();
}